#include <sxQViewport.h>
#include <QMouseEvent>
#include <QTimer>
#include <sxViewportController.h>

Sx::QtInterface::QViewport::QViewport(QWidget* parent, const std::shared_ptr<GraphicsContext>& context, unsigned int fps, Qt::WFlags flags) : QWidget(parent, flags), Viewport(fps) {
	this->setMouseTracking(true);
	this->updateTimer = nullptr;

	//--------------------------------------------------------------------------
	// If the frames per second is not 0 then connect a timer to this viewport
	// such that it will be reRendered at the desired frame rate.
	//--------------------------------------------------------------------------
	static int viewportCount = 0;
	if ( fps != 0 ) {
		this->realtime = true;
		this->updateTimer = new QTimer(this);
		this->connect(this->updateTimer, SIGNAL(timeout()), this, SLOT(onUpdateTimerTimeout()));
		this->updateTimer->setInterval(Viewport::CalculateMillisecondIncrement(this->framesPerSecond));
		std::cout << "[QViewport-" << viewportCount << "] Initializing Frame Time (" << this->framesPerSecond << "fps): " << Viewport::CalculateMillisecondIncrement(this->framesPerSecond) << "[ms]" << std::endl;
		this->updateTimer->start();
		viewportCount++;
	}
}

Sx::QtInterface::QViewport::~QViewport() {
	/* this->updateTimer memory handled by Qt */
}

void Sx::QtInterface::QViewport::update() {
	if ( this->controller == nullptr ) return;

	float dt = Viewport::CalculateMillisecondIncrement(this->framesPerSecond);
	this->controller->update(dt);
	if ( this->controller->isPaused() == false ) this->controller->incrementFrameIndex();
}

void Sx::QtInterface::QViewport::update(float dt) {
	if ( this->controller == nullptr ) return;
	this->controller->update(dt);
	if ( this->controller->isPaused() == false ) this->controller->incrementFrameIndex();
}

void Sx::QtInterface::QViewport::updateAndRender() {
	float dt = 0.0f;
	if ( realtime == true ) dt = Viewport::CalculateMillisecondIncrement(this->framesPerSecond);

	this->update(dt);
	this->reRender();
}

void Sx::QtInterface::QViewport::resize(int width, int height) {
	QWidget::resize(width, height);
}

void Sx::QtInterface::QViewport::mousePressEvent(QMouseEvent* e) {
	if ( e == nullptr ) {
		std::cerr << "[QViewport:mousePressEvent] Error: Nullptr mouse event e." << std::endl;
		return;
	}

	this->setFocus();

	MouseButton button;
	ModifierKey modifierKey;
	QViewport::TranslateMouseEvent(e, button, modifierKey);

	this->mousePressPosition.x() = e->x();
	this->mousePressPosition.y() = e->y();

	if ( this->controller == nullptr ) return;
	this->controller->setMousePressPosition(this->mousePressPosition.x(), this->mousePressPosition.y());
	this->controller->mousePressEvent(e->x(), e->y(), button, modifierKey);

	//--------------------------------------------------------------------------
	// Re-render the viewport if it should be updated by this mouse event
	// provided by the user (Real-time should be false since there shouldn't be
	// user-driven events and a realtime update loop at the same time).
	//--------------------------------------------------------------------------
	if ( this->userUpdates == true && this->realtime == false ) this->reRender();
}

void Sx::QtInterface::QViewport::mouseReleaseEvent(QMouseEvent* e) {
	if ( e == nullptr ) {
		std::cerr << "[QViewport:mouseReleaseEvent] Error: Nullptr mouse event e." << std::endl;
		return;
	}

	this->setFocus();

	MouseButton button;
	ModifierKey modifierKey;
	QViewport::TranslateMouseEvent(e, button, modifierKey);

	if ( this->controller == nullptr ) return;
	this->controller->setMouseReleasePosition(e->x(), e->y());
	this->controller->mouseReleaseEvent(e->x(), e->y(), button, modifierKey);

	if ( e->x() == this->mousePressPosition.x() && e->y() == this->mousePressPosition.y() )
		this->controller->mouseClickEvent(e->x(), e->y(), button, modifierKey);
	
	//--------------------------------------------------------------------------
	// Re-render the viewport if it should be updated by this mouse event
	// provided by the user (Real-time should be false since there shouldn't be
	// user-driven events and a realtime update loop at the same time).
	//--------------------------------------------------------------------------
	if ( this->userUpdates == true && this->realtime == false ) this->reRender();
}

void Sx::QtInterface::QViewport::mouseMoveEvent(QMouseEvent* e) {
	if ( e == nullptr ) {
		std::cerr << "[QViewport:mouseMoveEvent] Error: Nullptr mouse event e." << std::endl;
		return;
	}

	Interface::MouseButton button;
	Interface::ModifierKey modifierKey;
	QViewport::TranslateMouseEvent(e, button, modifierKey);

	if ( this->controller == nullptr ) return;
	this->controller->setMouseCurrentPosition(e->x(), e->y());
	this->controller->mouseMoveEvent(e->x(), e->y(), button, modifierKey);
	this->controller->updateMousePositionDifference();
	this->controller->updateMouseLastPosition();

	//--------------------------------------------------------------------------
	// Re-render the viewport if it should be updated by this mouse event
	// provided by the user (Real-time should be false since there shouldn't be
	// user-driven events and a realtime update loop at the same time).
	//--------------------------------------------------------------------------
	if ( this->userUpdates == true && this->realtime == false ) this->reRender();
}

void Sx::QtInterface::QViewport::wheelEvent(QWheelEvent* e) {
	if ( e == nullptr ) {
		std::cerr << "[QViewport:wheelEvent] Error: Nullptr mouse event e." << std::endl;
		return;
	}

	this->setFocus();

	if ( this->controller == nullptr ) return;
	this->controller->mouseWheelEvent(e->delta());

	//--------------------------------------------------------------------------
	// Re-render the viewport if it should be updated by this mouse event
	// provided by the user (Real-time should be false since there shouldn't be
	// user-driven events and a realtime update loop at the same time).
	//--------------------------------------------------------------------------
	if ( this->userUpdates == true && this->realtime == false ) this->reRender();
}

void Sx::QtInterface::QViewport::enterEvent(QEvent* e) {
	if ( e == nullptr ) {
		std::cerr << "[QViewport:enterEvent] Error: Nullptr mouse event e." << std::endl;
		return;
	}

	if ( this->controller == nullptr ) return;
	this->controller->mouseEnterEvent();
}

void Sx::QtInterface::QViewport::leaveEvent(QEvent* e) {
	if ( e == nullptr ) {
		std::cerr << "[QViewport:leaveEvent] Error: Nullptr mouse event e." << std::endl;
		return;
	}

	if ( this->controller == nullptr ) return;
	this->controller->mouseExitEvent();
}

void Sx::QtInterface::QViewport::focusInEvent(QFocusEvent* e) {
	if ( e == nullptr ) {
		std::cerr << "[QViewport:focusInEvent] Error: Nullptr mouse event e." << std::endl;
		return;
	}

	if ( this->controller == nullptr ) return;
	this->controller->select();

	//--------------------------------------------------------------------------
	// Re-render the viewport if it should be updated by this mouse event
	// provided by the user (Real-time should be false since there shouldn't be
	// user-driven events and a realtime update loop at the same time).
	//--------------------------------------------------------------------------
	if ( this->userUpdates == true && this->realtime == false ) this->reRender();
}

void Sx::QtInterface::QViewport::focusOutEvent(QFocusEvent* e) {
	if ( e == nullptr ) {
		std::cerr << "[QViewport:focusOutEvent] Error: Nullptr mouse event e." << std::endl;
		return;
	}

	if ( this->controller == nullptr ) return;
	this->controller->deselect();

	//--------------------------------------------------------------------------
	// Re-render the viewport if it should be updated by this mouse event
	// provided by the user (Real-time should be false since there shouldn't be
	// user-driven events and a realtime update loop at the same time).
	//--------------------------------------------------------------------------
	if ( this->userUpdates == true && this->realtime == false ) this->reRender();
}

/*
 * Translate the Qt based event constants to the platform-independent constants 
 * that can be transferred to the ViewportController.
 */
bool Sx::QtInterface::QViewport::TranslateMouseEvent(QMouseEvent* e, MouseButton& button, ModifierKey& modifierKey) {
	if ( e == nullptr ) {
		std::cerr << "[QViewport:TranslateMouseEvent] Error: Mouse Event e is a nullptr." << std::endl;
		return false;
	}

	if ( e->modifiers() == Qt::ControlModifier ) modifierKey = Sx::Interface::MODIFIER_CONTROL;
	else if ( e->modifiers() == Qt::AltModifier ) modifierKey = Sx::Interface::MODIFIER_ALT;
	else if ( e->modifiers() == Qt::ShiftModifier ) modifierKey = Sx::Interface::MODIFIER_SHIFT;
	else modifierKey = Sx::Interface::MODIFIER_NONE;

	if ( e->buttons() == (Qt::LeftButton | Qt::RightButton) ) button = Sx::Interface::LEFT_AND_RIGHT_BUTTONS;
	else if ( e->buttons() == Qt::LeftButton ) button = Sx::Interface::LEFT_MOUSE_BUTTON;
	else if ( e->buttons() == Qt::RightButton ) button = Sx::Interface::RIGHT_MOUSE_BUTTON;
	else if ( e->buttons() == Qt::MiddleButton ) button = Sx::Interface::MIDDLE_MOUSE_BUTTON;
	else button = Sx::Interface::NO_MOUSE_BUTTON;
	return true;
}

void Sx::QtInterface::QViewport::onUpdateTimerTimeout() {
	if ( this->realtime == true ) this->updateAndRender();
}
